using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;

namespace SPTraceView.Model
{
    internal static class ETW
    {
        internal const uint EVENT_TRACE_CONTROL_QUERY = 0;
        internal const uint EVENT_TRACE_CONTROL_STOP = 1;

        internal const int TRACE_LEVEL_VERBOSE = 5;
        internal const int WNODE_FLAG_TRACED_GUID = 0x20000;
        internal const ulong INVALID_HANDLE_VALUE = ulong.MaxValue;
        internal const int EVENT_TRACE_REAL_TIME_MODE = 0x100;
        internal const int CLOCK_RESOLUTION_QUERY_PERFORMANCE_COUNTER = 1;
        internal const int ERROR_ALREADY_EXISTS = 183;
        internal const int ERROR_CTX_CLOSE_PENDING = 7007;
        internal const int ERROR_BUSY = 170;

        [DllImport("Advapi32.dll", CharSet = CharSet.Unicode)]
        public static extern uint CloseTrace(ulong traceHandle);

        [DllImport("Advapi32.dll", EntryPoint = "ControlTraceW", CharSet = CharSet.Unicode)]
        public static extern uint ControlTrace(ulong traceHandle, string instanceName, ref EVENT_TRACE_PROPERTIES_WITH_NAMES properties, uint controlCode);
        
        [DllImport("Advapi32.dll", CharSet = CharSet.Unicode)]
        public static extern uint EnableTrace(uint enable, uint enableFlag, uint enableLevel, ref Guid controlGuid, ulong traceHandle);
        
        //[DllImport("Kernel32.dll")]
        //public static extern void GetSystemTimeAsFileTime(out FILETIME lpSystemTimeAsFileTime);
        
        [DllImport("Advapi32.dll", EntryPoint = "OpenTraceW", CharSet = CharSet.Unicode, SetLastError = true)]
        public static extern ulong OpenTrace(ref EVENT_TRACE_LOGFILE logfile);
        
        [DllImport("Advapi32.dll", CharSet = CharSet.Unicode)]
        public static extern uint ProcessTrace(ulong[] handleArray, uint handleCount, IntPtr startTime, IntPtr endTime);
        
        [DllImport("Advapi32.dll", EntryPoint = "StartTraceW", CharSet = CharSet.Unicode)]
        public static extern uint StartTrace(out ulong traceHandle, string instanceName, ref EVENT_TRACE_PROPERTIES_WITH_NAMES properties);

        [DllImport("Advapi32.dll", CharSet = CharSet.Unicode)]
        public static extern uint SetTraceCallback(Guid classGuid, ETW.EVENT_CALLBACK eEventCallback);


        public delegate void EVENT_CALLBACK(ref ETW.EVENT_TRACE eventTrace);

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        public struct EVENT_TRACE
        {
            public ETW.EVENT_TRACE_HEADER Header;
            public uint InstanceId;
            public uint ParentInstanceId;
            public Guid ParentGuid;
            public IntPtr MofData;
            public uint MofLength;
            private uint ClientContext;
        }

        public delegate uint EVENT_TRACE_BUFFER_CALLBACK(ref ETW.EVENT_TRACE_LOGFILE logfile);

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        public struct EVENT_TRACE_HEADER
        {
            public ushort Size;
            private ushort FieldTypeFlags;
            public byte Type;
            public byte Level;
            public ushort Version;
            public uint ThreadId;
            public uint ProcessId;
            public long TimeStamp;
            public Guid Guid;
            public ulong ProcessorTime;
        }

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        public struct EVENT_TRACE_LOGFILE
        {
            [MarshalAs(UnmanagedType.LPWStr)]
            public string LogFileName;
            [MarshalAs(UnmanagedType.LPWStr)]
            public string LoggerName;
            public long CurrentTime;
            public uint BuffersRead;
            public uint LogFileMode;
            public ETW.EVENT_TRACE CurrentEvent;
            public ETW.TRACE_LOGFILE_HEADER LogfileHeader;
            [MarshalAs(UnmanagedType.FunctionPtr)]
            public ETW.EVENT_TRACE_BUFFER_CALLBACK BufferCallback;
            public uint BufferSize;
            public uint Filled;
            public uint EventsLost;
            [MarshalAs(UnmanagedType.FunctionPtr)]
            public ETW.EVENT_CALLBACK EventCallback;
            public uint IsKernelTrace;
            private IntPtr Context;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct EVENT_TRACE_PROPERTIES
        {
            public ETW.WNODE_HEADER Wnode;
            public uint BufferSize;
            public uint MinimumBuffers;
            public uint MaximumBuffers;
            public uint MaximumFileSize;
            public uint LogFileMode;
            public uint FlushTimer;
            public uint EnableFlags;
            public int AgeLimit;
            public uint NumberOfBuffers;
            public uint FreeBuffers;
            public uint EventsLost;
            public uint BuffersWritten;
            public uint LogBuffersLost;
            public uint RealTimeBuffersLost;
            public IntPtr LoggerThreadId;
            public uint LogFileNameOffset;
            public uint LoggerNameOffset;
        }

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        public struct EVENT_TRACE_PROPERTIES_WITH_NAMES
        {
            public ETW.EVENT_TRACE_PROPERTIES EventTraceProperties;

            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x400)]
            public string LoggerName;

            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x400)]
            public string LogFileName;

            public static ETW.EVENT_TRACE_PROPERTIES_WITH_NAMES Create()
            {
                EVENT_TRACE_PROPERTIES_WITH_NAMES instance = new EVENT_TRACE_PROPERTIES_WITH_NAMES();
                Marshal.SizeOf(typeof(ETW.WNODE_HEADER));
                instance.EventTraceProperties.Wnode.BufferSize = (uint)Marshal.SizeOf(typeof(ETW.EVENT_TRACE_PROPERTIES_WITH_NAMES));
                instance.EventTraceProperties.Wnode.Flags = ETW.WNODE_FLAG_TRACED_GUID;
                instance.EventTraceProperties.LoggerNameOffset = (uint)Marshal.SizeOf(typeof(ETW.EVENT_TRACE_PROPERTIES));
                instance.EventTraceProperties.LogFileNameOffset = (uint)(instance.EventTraceProperties.LoggerNameOffset + instance.EventTraceProperties.Wnode.BufferSize) / 2;

                return instance;
            }
        }

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        public struct SYSTEMTIME
        {
            public short wYear;
            public short wMonth;
            public short wDayOfWeek;
            public short wDay;
            public short wHour;
            public short wMinute;
            public short wSecond;
            public short wMilliseconds;
        }

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        public struct TIME_ZONE_INFORMATION
        {
            public int Bias;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)]
            public string StandardName;
            public ETW.SYSTEMTIME StandardDate;
            public int StandardBias;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)]
            public string DaylightName;
            public ETW.SYSTEMTIME DaylightDate;
            public int DaylightBias;
        }

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        public struct TRACE_LOGFILE_HEADER
        {
            public uint BufferSize;
            public byte MajorVersion;
            public byte MinorVersion;
            public byte SubVersion;
            public byte SubMinorVersion;
            public uint ProviderVersion;
            public uint NumberOfProcessors;
            public long EndTime;
            public uint TimerResolution;
            public uint MaximumFileSize;
            public uint LogFileMode;
            public uint BuffersWritten;
            public uint StartBuffers;
            public uint PointerSize;
            public uint EventsLost;
            public uint CpuSpeedInMHz;
            public IntPtr LoggerName;
            public IntPtr LogFileName;
            public ETW.TIME_ZONE_INFORMATION TimeZone;
            public long BootTime;
            public long PerfFreq;
            public long StartTime;
            public uint ReservedFlags;
            public uint BuffersLost;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct WNODE_HEADER
        {
            public uint BufferSize;
            public uint ProviderId;
            public ulong HistoricalContext;
            public uint CountLost;
            private uint reserved;
            public Guid Guid;
            public uint ClientContext;
            public uint Flags;
        }
    }

}
